package com.zlyx.easy.swagger.plugins;

import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toCollection;
import static org.springframework.core.annotation.AnnotationUtils.findAnnotation;
import static springfox.documentation.service.Tags.emptyTags;
import static springfox.documentation.swagger.common.SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER;
import static springfox.documentation.swagger.common.SwaggerPluginSupport.pluginDoesApply;

import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Stream;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.zlyx.easy.swagger.annotations.SpringController;

import io.swagger.annotations.Api;
import springfox.documentation.builders.BuilderDefaults;
import springfox.documentation.service.Tag;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ApiListingBuilderPlugin;
import springfox.documentation.spi.service.contexts.ApiListingContext;

@Component
@Order(value = SWAGGER_PLUGIN_ORDER + 1)
public class EasySwaggerApiListingReader implements ApiListingBuilderPlugin {
	@Override
	public void apply(ApiListingContext apiListingContext) {
		Optional<? extends Class<?>> controller = apiListingContext.getResourceGroup().getControllerClass();
		if (controller.isPresent()) {
			Optional<Api> apiAnnotation = ofNullable(findAnnotation(controller.get(), Api.class));
			Set<Tag> oasTags = tagsFromOasAnnotations(apiListingContext.getResourceGroup().getGroupName(),
					controller.get());
			String description = apiAnnotation.map(Api::description).map(BuilderDefaults::emptyToNull).orElse(null);

			Set<String> tagSet = new TreeSet<>();
			if (oasTags.isEmpty()) {
				tagSet.addAll(apiAnnotation.map(tags()).orElse(new TreeSet<>()));
				if (tagSet.isEmpty()) {
					tagSet.add(apiListingContext.getResourceGroup().getGroupName());
				}
			}
			apiListingContext.apiListingBuilder().description(description).tagNames(tagSet).tags(oasTags);
		}
	}

	private Set<Tag> tagsFromOasAnnotations(String groupName, Class<?> controller) {
		HashSet<Tag> controllerTags = new HashSet<>();
		SpringController springController = findAnnotation(controller, SpringController.class);
		if (springController != null) {
			controllerTags.add(new Tag(groupName, springController.todo()[0]));
		}
		return controllerTags;
	}

	private Function<Api, Set<String>> tags() {
		return input -> Stream.of(input.tags()).filter(emptyTags()).collect(toCollection(TreeSet::new));
	}

	@Override
	public boolean supports(DocumentationType delimiter) {
		return pluginDoesApply(delimiter);
	}
}
